﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using SharpDiary.Core.Common;

namespace SharpDiary.Core.Security
{
    public class DES : ICryptionService
    {
        private string _key = string.Empty;

        public string Key
        {
            get { return _key; }
            set { _key = value; }
        }

        private static byte[] ToMD5(string text)
        {
            using (MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider())
            {
                byte[] input = Encoding.UTF8.GetBytes(text);
                return provider.ComputeHash(input);
            }
        }

        /// <summary>
        /// Encrypt a string.
        /// </summary>
        /// <param name="originalString">The original string.</param>
        /// <returns>The encrypted string.</returns>
        /// <exception cref="ArgumentNullException">This exception will be thrown when the original string is null or empty.</exception>
        public string Encrypt(string originalString)
        {
            if (string.IsNullOrEmpty(originalString)) { return string.Empty; }
            if (string.IsNullOrEmpty(_key)) { return string.Empty; }

            string result = string.Empty;

            //将 key 转化为 MD5 bytes 后，取前 8 bytes 作为 DES 加解密的 key.
            byte[] md5Bytes = ToMD5(_key);
            byte[] keyBytes = new byte[8];
            Array.Copy(md5Bytes, keyBytes, 8);

            using (DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider())
            {
                try
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(keyBytes, keyBytes), CryptoStreamMode.Write))
                        {

                            using (StreamWriter writer = new StreamWriter(cryptoStream))
                            {
                                writer.Write(originalString);
                                writer.Flush();
                                cryptoStream.FlushFinalBlock();
                                writer.Flush();

                                result = Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
                            }
                        }
                    }
                }
                catch
                {
                    cryptoProvider.Clear();
                    return originalString;
                }

                cryptoProvider.Clear();
                return result;
            }
        }

        /// <summary>
        /// Decrypt a crypted string.
        /// </summary>
        /// <param name="cryptedString">The crypted string.</param>
        /// <returns>The decrypted string.</returns>
        /// <exception cref="ArgumentNullException">This exception will be thrown when the crypted string is null or empty.</exception>
        public string Decrypt(string cryptedString)
        {
            if (string.IsNullOrEmpty(cryptedString)) { return string.Empty; }
            if (string.IsNullOrEmpty(_key)) { return string.Empty; }

            string result = string.Empty;

            //将 key 转化为 MD5 bytes 后，取前 8 bytes 作为 DES 加解密的 key.
            byte[] md5Bytes = ToMD5(_key);
            byte[] keyBytes = new byte[8];
            Array.Copy(md5Bytes, keyBytes, 8);

            using (DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider())
            {
                try
                {
                    using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cryptedString)))
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateDecryptor(keyBytes, keyBytes), CryptoStreamMode.Read))
                        {
                            using (StreamReader reader = new StreamReader(cryptoStream))
                            {
                                result = reader.ReadToEnd();
                            }
                        }
                    }
                }
                catch
                {
                    cryptoProvider.Clear();
                    return cryptedString;
                }

                cryptoProvider.Clear();
                return result;
            }
        }
    }
}
